home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 11 - 1995 / 11.10 Oct 95 / 11.10 Tips & Tidbits / 11.11 Tips Text
Encoding:
Text File  |  1995-09-27  |  10.4 KB  |  311 lines  |  [TEXT/ttxt]

  1. This is the text of an article orginially printed in MacTech Magazine™.  MacTech Magazine, for Macintosh Programmers and Developers.  For subscription information, please contact us at:
  2.   •  http://www.mactech.com
  3.   •  mailto:info@mactech.com
  4.   •  805/494-9797
  5.  
  6.  
  7. Tips and Tidbits
  8. edited by Steve Sisak
  9.  
  10. Spotting the Elusive Ram Disk
  11.  
  12. Here is a handy function for performing a dynamic check for the existence of a RAM disk as created by Apple’s Memory control panel. I use it in some of my programs when speed is paramount. Since the user can remove the RAM disk at any time by turning it off from the Memory control panel, and can rename it at any time, the code here must be called before using the RAM disk. In other words, don’t check just once during your application startup and assume thereafter that it will still be there, check before each access if possible. Program defensively...
  13. – Greg Poole
  14.  
  15.  
  16. HasRamDisk.h
  17. #pragma once
  18.  
  19. #ifdef __cplusplus
  20.     extern "C" {
  21. #endif
  22.     extern Boolean        HasRamDisk( FSSpecPtr ramDiskSpec );
  23. #ifdef __cplusplus
  24.     }
  25. #endif
  26.  
  27. HasRamDisk.c
  28. /******************************************************************************
  29.      HasRamDisk.c
  30.  
  31.     A dynamic check for the existence of a RAM disk as created by Apple's
  32.     Memory control panel. Since the user can remove the RAM disk at any time
  33.     by turning it off from the Memory control panel, and can rename it at any
  34.     time, the code here needs to be called before assuming the existence of
  35.     a RAM disk. In other words, don't check just once during your application
  36.     startup and assume thereafter that it will still be there, check 
  37.     before each access. Program defensively...
  38.     
  39.     history:
  40.  
  41.         modified:    xx/xx/xx        who are you? what did you do?
  42.         created:        08/10/94            greg poole
  43.  
  44.     Greg Poole
  45.     Vital Images, Inc.
  46.     505 N. 4th Street
  47.     Fairfield, IA 52556
  48.     (515) 472-7726
  49.     email: greg@vitalimages.com
  50.  
  51.  ******************************************************************************/
  52.  
  53. #include <string.h>
  54. #include "HasRamDisk.h"
  55.  
  56. // this structure is based on the DRVR definition in MPWTypes.r
  57. //
  58. struct DRVRresourceRec
  59. {
  60.     // description of drvrFlags
  61.     //
  62.     //    struct
  63.     //    {
  64.     //        unsigned hiUnused     : 1;    // unused
  65.     //        unsigned needLock     : 1;    // lock drvr in memory
  66.     //        unsigned needTime     : 1;    // for periodic action
  67.     //        unsigned needGoodbye  : 1;    // call before heap reinit
  68.     //        unsigned statusEnable : 1;    // responds to status
  69.     //        unsigned ctlEnable    : 1;         // responds to control
  70.     //        unsigned writeEnable  : 1;    // responds to write
  71.     //        unsigned readEnable   : 1;    // responds to read
  72.     //        unsigned loUnused     : 8;    // low byte of drvrFlags word unused
  73.     //    } drvrFlags;
  74.     
  75.     short            drvrFlags;                // flags as defined above    
  76.     unsigned short    driverDelay;    // driver delay (ticks)
  77.     short            deskAccEventMask;    // desk acc event mask
  78.     short            driverMenuID;            // driver menu ID
  79.  
  80.     unsigned short     offsetOpen;        // offset to DRVRRuntime open
  81.     unsigned short     offsetPrime;    // offset to DRVRRuntime prime
  82.     unsigned short     offsetControl;// offset to DRVRRuntime control
  83.     unsigned short     offsetStatus;    // offset to DRVRRuntime status
  84.     unsigned short     offsetClose;    // offset to DRVRRuntime close
  85.  
  86.     Str31        driverName;                // driver name
  87.     char            driverCode[1];         // driver code
  88. };
  89. typedef struct DRVRresourceRec DRVRresourceRec;
  90. typedef DRVRresourceRec *DRVRresourcePtr, **DRVRresourceHndl;
  91.  
  92. // constants
  93. //
  94. const char kDrvrHandleBit = 0x40;        
  95.                                         // bit 7 of 'DRVR' dCtlFlags signals driver is handle
  96.                                         // instead of pointer and needs to be locked in memory
  97. const char kRamDiskName[] = "\p.EDisk";    // Apple's RAM disk driver name
  98.  
  99.  
  100. // pass in an FSSpecPtr to hold a reference to a RAM disk,
  101. // returns TRUE if there is currently a RAM disk, FALSE if not
  102. //
  103. Boolean    HasRamDisk( FSSpecPtr ramDiskSpec )
  104. {
  105.     Boolean                    hasRamDisk = FALSE, isHandle = FALSE;
  106.     short                         whichVol = 1;        // start with first disk volume
  107.     HVolumeParam            volPB;
  108.     OSErr                        theErr = noErr, anErr = noErr;
  109.     DCtlHandle                dctlHndl = NULL;
  110.     DRVRresourcePtr        drvrPtr = NULL;
  111.     DRVRresourceHndl    drvrHndl = NULL;
  112.     Ptr                            aPtr = NULL;
  113.     Str31                        volName;
  114.  
  115.     do                                    // test each mounted disk volume
  116.     {
  117.         volPB.ioNamePtr = volName;
  118.         volPB.ioVRefNum = 0;            // 0 means use ioVolIndex
  119.         volPB.ioVolIndex = whichVol;    // use this to determine volume
  120.     
  121.         if ( (theErr=PBHGetVInfoSync( (HParmBlkPtr)&volPB )) == noErr)
  122.         {
  123.             // get this volume's device control entry from the unit table.
  124.             // do not lock the dctlHndl, I spent a couple of days figuring
  125.             // out that locking this handle causes a crash in the CompServer
  126.             // because it is locked at interrupt time...
  127.             //
  128.             if ( (dctlHndl = GetDCtlEntry(volPB.ioVDRefNum)) != NULL )
  129.             {
  130.                 // is the device's driver in a handle or a pointer?
  131.                 //
  132.                 if ((isHandle=(*dctlHndl)->dCtlFlags&kDrvrHandleBit)!= 0)
  133.                 {
  134.                     drvrHndl = (DRVRresourceHndl) (*dctlHndl)->dCtlDriver;
  135.                     drvrPtr = *drvrHndl;
  136.                 }
  137.                 else
  138.                     drvrPtr = (DRVRresourcePtr) (*dctlHndl)->dCtlDriver;
  139.  
  140.                 // get this device's driver, check if it is a RAM disk
  141.                 //
  142.                 if ( !memcmp( drvrPtr->driverName, kRamDiskName,  
  143.               *kRamDiskName+1 ) )
  144.                 {
  145.                     // this driver is the RAM disk driver, create an FSSpec to its root dir
  146.                     //
  147.                     anErr = FSMakeFSSpec( volPB.ioVRefNum, fsRtDirID, 
  148.                                  volName, ramDiskSpec );
  149.                     if ( anErr == noErr )
  150.                         hasRamDisk = TRUE;
  151.                     break;
  152.                 }
  153.             }
  154.         }
  155.         whichVol++; // go to next volume
  156.     } 
  157.     while ( theErr != nsvErr );
  158.  
  159.     return hasRamDisk;
  160.     
  161. } // end HasRamDisk
  162.  
  163.  
  164. // define TEST_RAM_DISK for a standalone test
  165. //
  166. #define TEST_RAM_DISK
  167.  
  168. #if defined( TEST_RAM_DISK )
  169.  
  170.     // local function prototypes
  171.     //
  172.     static void InitTheMac( void );
  173.     
  174.     static void InitTheMac( void )
  175.     {
  176.         InitGraf( &qd.thePort );
  177.         InitFonts();
  178.         InitWindows();
  179.         InitMenus();
  180.         TEInit();
  181.         InitDialogs( 0L );
  182.         InitCursor();
  183.         MaxApplZone();
  184.     
  185.     } // end InitTheMac
  186.  
  187.     void main( void )
  188.     {
  189.         FSSpec            ramDiskSpec;        
  190.         Boolean        hasRamDisk;
  191.         
  192.         InitTheMac();
  193.         hasRamDisk = HasRamDisk( &ramDiskSpec );
  194.         
  195.     } // end main
  196.  
  197. #endif // TEST_RAM_DISK
  198.  
  199.  
  200. Anti-Tip of the Month
  201. Since Greg Poole won the Tip-of-the-Month I thought we could have a little fun and also give him the Anti-Tip-of-the-Month as well for a different submission. (Don’t worry, Greg, you’re getting paid for this too.)
  202. Greg writes:
  203.  
  204. Here’s a quick and clean way to swap data in place without having to resort to using a temporary memory location:
  205.  
  206.                    short *aPtr, *bPtr;
  207.  
  208.                 *aPtr ^= *bPtr;
  209.                 *bPtr ^= *aPtr;
  210.                 *aPtr ^= *bPtr;
  211. While this is mathematically cool, lets take a look at the assembly code that it generates and see what’s really happening. First, for comparison, a couple of more pedestrian implementations:
  212.  
  213. void swap2(short *aPtr, short *bPtr)
  214. {
  215.     short a = *aPtr;    // a version with two temporaries
  216.     short b = *bPtr;
  217.  
  218.     *aPtr = b;
  219.     *bPtr = a;
  220. }
  221.  
  222. void swap1(short *aPtr, short *bPtr)
  223. {
  224.     short a = *aPtr;    // a version with one temporary
  225.  
  226.     *aPtr = *bPtr;
  227.     *bPtr = a;
  228. }
  229.  
  230. void swap0(short *aPtr, short *bPtr)
  231. {
  232.     *aPtr ^= *bPtr;     // Greg’s tip
  233.     *bPtr ^= *aPtr;
  234.     *aPtr ^= *bPtr;
  235. }
  236. Now, let’s take a look at what the compiler actually generates for these functions. (I’m using CodeWarrior with all optimizations on for these examples.)
  237. Recall that as processsors have gotten faster, memory has not. For instance 1/80ns (the speed on memory in most Macintoshes) = 12.5 MHz. This means that if adjacent instructions have to address memory with no intervening computation, it’s as if the processor has slowed to 12.5MHz.
  238. First the 68K compiler, starting with the two temp case:
  239.  
  240. Name="swap2"(6)  Size=26
  241.     MOVEA.L   $0004(A7),A1
  242.     MOVEA.L   $0008(A7),A0
  243.     MOVE.W    (A1),D0
  244.     MOVE.W    (A0),D1
  245.     MOVE.W    D1,(A1)
  246.     MOVE.W    D0,(A0)
  247.     RTS
  248. Ignoring the two MOVEA.L’s which set up the address registers and the return, this takes four instructions, all of which touch memory. Notice, however that there are no cases where the result of an instruction is used an an input to the next instruction, meaning that most of the instructions can overlap in the processor pipeline.
  249. Next with one temp:
  250.  
  251. Name="swap1"(4)  Size=24
  252.     MOVEA.L   $0004(A7),A1
  253.     MOVEA.L   $0008(A7),A0
  254.     MOVE.W    (A1),D0
  255.     MOVE.W    (A0),(A1)
  256.     MOVE.W    D0,(A0)
  257.     RTS
  258. Here we have three instructions, all accessing memory and all can overlap.  This is smaller than the example above. Whether it is faster depends on the relative timing of the  MOVE.W (A0),(A1) instruction. (If anyone wants to time this, I’ll print the results.)
  259. Now Greg’s ‘tip’:
  260.  
  261. Name="swap0"(1)  Size=30
  262.     MOVEA.L   $0004(A7),A1
  263.     MOVEA.L   $0008(A7),A0
  264.     MOVE.W    (A0),D0
  265.     EOR.W     D0,(A1)
  266.     MOVE.W    (A1),D0
  267.     EOR.W     D0,(A0)
  268.     MOVE.W    (A0),D0
  269.     EOR.W     D0,(A1)
  270.     RTS
  271.  
  272. This generates six instructions, all of which touch memory. Furthermore three of these are read-modify-write cycles, which are slower that a read or write and each instruction depends on the result of the instructon directly before it, meaning it won’t overlap in the pipeline, making this both the largest and slowest implementation of the three.
  273. Now lets look at the PowerPC code:
  274. Name=".swap2"(6)  Size=20
  275.     lha      r0,0(r3)
  276.     lha      r5,0(r4)
  277.     sth      r5,0(r3)
  278.     sth      r0,0(r4)
  279.     blr
  280.  
  281. Name=".swap1"(4)  Size=20
  282.     lha      r5,0(r3)
  283.     lha      r0,0(r4)
  284.     sth      r0,0(r3)
  285.     sth      r5,0(r4)
  286.     blr
  287.  
  288. Note that both of the versions with temporaries generated the same code (4 instructions, all touching memory but pipelineable). This is because RISC processors typically don’t have memory to memory operations; instead, they must move data to a register before operating on it.
  289. Now our ‘tip’:
  290.  
  291. Name=".swap0"(1)  Size=52
  292.     lha      r5,0(r4)
  293.     lha      r0,0(r3)
  294.     xor      r0,r0,r5
  295.     sth      r0,0(r3)
  296.     lha      r5,0(r3)
  297.     lha      r0,0(r4)
  298.     xor      r0,r0,r5
  299.     sth      r0,0(r4)
  300.     lha      r4,0(r4)
  301.     lha      r0,0(r3)
  302.     xor      r0,r0,r4
  303.     sth      r0,0(r3)
  304.     blr
  305.  
  306. This implementation is by far the largest and slowest, generating 12 instructions, including 6 memory accesses. Furthermore there are 2 pipeline stalls. Clearly this implementation is the largest and slowest of all.
  307. The moral of the story is: don’t get tricky. C programmers often try to minimize the number of lines of C in their program without consideration for what the compiler will generate. When in doubt, write clear code and give the optimizer a chance to maximize performance. Look at the compiler output. Your code will be easier to debug and probably faster too.
  308. ’Till next time,
  309.  
  310. – Steve
  311.